package cn.xkai.exercise.a;

import java.util.HashMap;
import java.util.Map;

/**
 * @description: 有效的数独
 * board.length == 9
 * board[i].length == 9
 * board[i][j] 是一位数字（1-9）或者 '.'
 *
 * 自己的思路：暴力遍历：横-竖-内层
 * 借鉴的思路：
 *      暴力遍历：同理。map.containsKey效率偏低，把数据放到数组中，判断是否重复即可
 *      位运算：位图？
 * @author: kaixiang
 * @date: 2022-07-04
 **/
public class Solution30 {
    public boolean isValidSudoku(char[][] board) {
        Map<Character, Integer> row = new HashMap<>(9);
        Map<Character, Integer> column = new HashMap<>(9);
        Map<Character, Integer> first = new HashMap<>(9);
        Map<Character, Integer> second = new HashMap<>(9);
        Map<Character, Integer> third = new HashMap<>(9);
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board.length; j++) {
                // 横-行
                if (board[i][j] != '.') {
                    if (row.containsKey(board[i][j])) {
                        return false;
                    }
                    row.put(board[i][j], 0);
                }
                // 竖-列
                if (board[j][i] != '.') {
                    if (column.containsKey(board[j][i])) {
                        return false;
                    }
                    column.put(board[j][i], 0);
                }
                // 内层-小9宫格
                if (board[i][j] == '.') {
                    continue;
                }
                if (j < 3) {
                    if (first.containsKey(board[i][j])) {
                        return false;
                    }
                    first.put(board[i][j], 0);
                } else if (j < 6) {
                    if (second.containsKey(board[i][j])) {
                        return false;
                    }
                    second.put(board[i][j], 0);
                } else {
                    if (third.containsKey(board[i][j])) {
                        return false;
                    }
                    third.put(board[i][j], 0);
                }
            }
            row = new HashMap<>(9);
            column = new HashMap<>(9);
            if ((i + 1) % 3 == 0) {
                first = new HashMap<>(9);
                second = new HashMap<>(9);
                third = new HashMap<>(9);
            }
        }
        return true;
    }

    public boolean isValidSudokuRefer(char[][] board) {
        int length = board.length;
        //二维数组line表示的是对应的行中是否有对应的数字，比如line[0][3]
        //表示的是第0行（实际上是第1行，因为数组的下标是从0开始的）是否有数字3
        int[][] line = new int[length][length];
        int[][] column = new int[length][length];
        int[][] cell = new int[length][length];
        for (int i = 0; i < length; ++i) {
            for (int j = 0; j < length; ++j) {
                //如果还没有填数字，直接跳过
                if (board[i][j] == '.') {
                    continue;
                }
                //num是当前格子的数字
                int num = board[i][j] - '0' - 1;
                //k是第几个单元格，9宫格数独横着和竖着都是3个单元格
                int k = i / 3 * 3 + j / 3;
                //如果当前数字对应的行和列以及单元格，只要一个由数字，说明冲突了，直接返回false。
                //举个例子，如果line[i][num]不等于0，说明第i（i从0开始）行有num这个数字。
                if (line[i][num] != 0 || column[j][num] != 0 || cell[k][num] != 0) {
                    return false;
                }
                //表示第i行有num这个数字，第j列有num这个数字，对应的单元格内也有num这个数字
                line[i][num] = column[j][num] = cell[k][num] = 1;
            }
        }
        return true;
    }

    public boolean isValidSudokuReferBit(char[][] board) {
        int[] line = new int[9];
        int[] column = new int[9];
        int[] cell = new int[9];
        int shift = 0;
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (board[i][j] == '.') {
                    continue;
                }
                shift = 1 << (board[i][j] - '0' - 1);
                int k = (i / 3) * 3 + j / 3;
                //如果对应的位置只要有一个大于0，说明有冲突，直接返回false
                if ((column[i] & shift) > 0 || (line[j] & shift) > 0 || (cell[k] & shift) > 0) {
                    return false;
                }
                column[i] |= shift;
                line[j] |= shift;
                cell[k] |= shift;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        char[][] board = {
                {'.', '.', '4', '.', '.', '.', '6', '3', '.'},
                {'.', '.', '.', '.', '.', '.', '.', '.', '.'},
                {'5', '.', '.', '.', '.', '.', '.', '9', '.'},
                {'.', '.', '.', '5', '6', '.', '.', '.', '.'},
                {'4', '.', '3', '.', '.', '.', '.', '.', '1'},
                {'.', '.', '.', '7', '.', '.', '.', '.', '.'},
                {'.', '.', '.', '5', '.', '.', '.', '.', '.'},
                {'.', '.', '.', '.', '.', '.', '.', '.', '.'},
                {'.', '.', '.', '.', '.', '.', '.', '.', '.'}
        };
        Solution30 solution30 = new Solution30();
        System.out.println(solution30.isValidSudokuReferBit(board));
    }
}
